﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;

using Akabana.Amf.Util;
using Akabana.Amf.Messaging;
using Akabana.Amf.Messaging.Gateway.Exception;

namespace Akabana.Amf.Messaging.Gateway.Service
{
    /**
     * <summary>リモーティングサーバ属性解決するクラス</summary>
     */
    public class MessagingServiceAttributeResolver
    {
        private const string SERVICE_IMPL_NAME_REG = @".+\.(.+)Service(Impl)?$";

        /**
         * <summary>リモーティングサービス属性が付与されているタイプのリストを取得する</summary>
         * <param name="assembly">アセンブリ</param>
         * <returns>タイプのリスト</returns>
         */
        public static Dictionary<String, Type> GetRemotingServices(Assembly assembly)
        {
            var types = assembly.GetTypes();
            var result = new Dictionary<String, Type>();

            foreach (var type in types)
            {
                if (IsServiceType(type))
                {
                    string serviceName = null;
                    var attributes = type.GetCustomAttributes(false);
                    foreach (var attr in attributes)
                    {
                        if (attr is MessagingServiceAttribute)
                        {
                            serviceName = (attr as MessagingServiceAttribute).Name;
                            break;
                        }
                    }
                    if( serviceName == null )
                    {
                        serviceName = GetServiceName(type);
                    }
                    if (serviceName != null)
                    {
                        result.Add(serviceName, type);
                    }
                }
            }

            return result;
        }

        /**
         * <summary>サービスメソッド属性が付与されているメソッドを取得する</summary>
         * <param name="serviceType">タイプ</param>
         * <param name="methodName">メソッド名</param>
         * <param name="args">引数</param>
         * <returns>メソッド情報</returns>
         */
        public static MethodInfo FindServiceMethodInfo(Type serviceType, string methodName,object[] args)
        {
            var paramTypes = new Type[args.Length];
            for (int i = 0; i < paramTypes.Length; i++)
			{
                if (args[i] != null)
                {
                    paramTypes[i] = args[i].GetType();
                }
                else
                {
                    paramTypes[i] = typeof(object);
                }
			}

            var result = MethodInfoUtil.GetMethod(serviceType, methodName, paramTypes);
            if (!IsServiceMethod(result))
            {
                throw new ServiceMethodNotFoundException(serviceType.FullName + "#" + methodName);
            }
            return result;
        }

        private static bool IsServiceType(Type type)
        {
            return type.IsPublic && !type.IsInterface && Regex.IsMatch(type.FullName, SERVICE_IMPL_NAME_REG);
        }

        private static bool IsServiceMethod(MethodInfo methodInfo)
        {
            return methodInfo != null && methodInfo.IsPublic;
        }

        private static string GetServiceName(Type type)
        {
            string result = type.Name;
            if (result.IndexOf("Impl") > 0)
            {
                result = result.Remove(result.IndexOf("Impl"));
            }

            var headChar = result.Substring(0, 1);
            var leftChat = result.Substring(1, result.Length - 1);

            return headChar.ToLower() + leftChat;
        }
    }
}
